import argparse
import json
import logging
import pickle
import datetime
from redislite import Redis
from tornado import websocket, web, ioloop
from wrenchbox.logging import setup_log
from ..test1.ledger import Ledger
from ..test1.transaction import Transaction

DEFAULTS = {'port': 9000}
clients = []
db = None

class IndexHandler(web.RequestHandler):
    def get(self):
        self.render("index.html")
        
class SocketHandler(websocket.WebSocketHandler):
    def check_origin(self, origin):
        return True
    def open(self):
        logging.info('Client connected:      %s',self.request.remote_ip)
        if self not in clients:
            clients.append(self)
    def on_close(self):
        if self in clients:
            clients.remove(self)
    def on_message(self, message):
        try:
            message = json.loads(message)
        except json.JSONDecodeError:
            logging.warning('Cannot parse request message: %s', message)
            self.write_message(json.dumps({
	        'status': 500,
		'error': 'Cannot parse request message.',
		'response': None
	    }))
        else:
    	    if message is not None:
                if 'op' in message:
                    if message['op'] == 'register':
                        
                        if'args' in message and 'addr' in message['args']:
                            peers = pickle.loads(db.get('peers'))
                            
                            if not isinstance(message['args']['addr'], list):
                                message['args']['addr'] = [str(message['args']['addr'])]
                            for addr in message['args']['addr']:
                                if addr.startswith('ws://') or addr.startswith('wss://'):
                                    peers.add(addr)
                            db.set('peers', pickle.dumps(peers))
                            self.write_message(json.dumps({
                                'status': 202,
                                'error': 'Accepted'
                                }))
                        else:
                            self.write_message(json.dumps({
                                'status': 500,
                                'error': 'Operation "register" requires the following "args": "addr"',
                                'response': None
                                }))
                                        
                    elif message['op'] == 'peers':
                        self.write_message(json.dumps({
                            'status': 200,
                            'error': 'OK',
                            'response': {'peers':list(pickle.loads(db.get('peers')))}
                            }))
                    elif message['op'] == 'time':
                        self.write_message(json.dumps({
                            'status' : 200,
                            'error':'OK',
                            'response':{
                                'time':datetime.datetime.now().timestamp(),
                                'time_zone':datetime.datetime.now(datetime.timezone.utc).astimezone().tzname()
                                }
                            }))
                    elif message['op'] == 'pool':
                        self.write_message(json.dumps({
                            'status': 200,
                            'error': 'OK',
                            'response': {
                                'pool': [json.loads(str(i)) for i in list(pickle.loads(db.get('pool')))]
                                }
                            }))
                    elif message['op'] == 'merge':
                        if 'args' in message and 'pool' in message['args']:
                            pool = pickle.loads(db.get('pool'))
                            for i in message['args']['pool']:
                                pool.add(Transaction(
                                    sender=i['sender'],
                                    receiver=i['receiver'],
                                    amount=i['amount']
                                    ))
                            db.set('pool', pickle.dumps(pool))
                            self.write_message(json.dumps({
                                'status': 202,
                                'error': 'Accepted'
                                }))
                        else:
                            self.write_message(json.dumps({
                                'status': 500,
                                'error': 'Operation "merge" requires the following "args": "pool"',
                                'response': None
                                }))
                    elif message['op'] == 'blocks':
                        blocks = json.loads(str(pickle.loads(db.get('ledger'))))['blocks']
                        if 'args' in message and 'start' in message['args']:
                            blocks = blocks[int(message['args']['start']):]
                        self.write_message(json.dumps({
                            'status': 200,
                            'error': 'OK',
                            'response': {
                                'blocks': blocks
                                }
                            }))
                    else: 
                        self.write_message(json.dumps({
                            'status': 404,
                            'error': 'Operation "{}" is not supported.'.format(message['op']),
                            'response': None
                            }))
                else:
                    logging.warning('Message body is not supported: %s', message)
                    self.write_message(json.dumps({
                        'status': 500,
                        'error': 'Message body is not supported.',
                        'response': None
                        }))

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument(
    	'--debug',
    	action='store_true',
    	default=False, help='show debug information'
    )
    
    parser.add_argument(
	'-p',
	'--port',
	type=int,
	default=DEFAULTS['port'],
	help='listening port, default: {}'.format(DEFAULTS['port'])
    )
    
    parser.add_argument(
	'-r',
	'--redis'
	,
	type=str,
	default='redis.db'
	,
	help='redis database file, default: redis.db'
    )
    args, _ = parser.parse_known_args()
    
    print(args)
    setup_log(level=logging.DEBUG if args.debug else logging.INFO)
    db = Redis(args.redis)
    if b'peers' not in db.keys():
        db.set('peers', pickle.dumps(set([])))
    if b'pool' not in db.keys():
        db.set('pool', pickle.dumps(set([])))
    if b'ledger' not in db.keys():
        db.set('ledger', pickle.dumps(Ledger()))
        
    web.Application([
        (r'/', IndexHandler),
        (r'/ws', SocketHandler)
    ]).listen(args.port)
    logging.info('Tornado is listening on port: %d', args.port)
    ioloop.IOLoop.instance().start()


